var FRONT_OR_BACK = "front";

/*
****************************************
* Objects used by the Scroller	*
****************************************
*/	
var currentContent;			// currently-visible DIV.  Necessary for the Scroller to be shared across DIVs
var currentContentStyle;	// style object of the currentContent
var scrollBar;				// Parent scrollbar DIV.  Contains the track and thumb
var scrollThumb;			// Scroller's thumb control
var scrollTrack;			// Scroller's base/track
var trackTimer;				// for extended mousedowns in the scroll track

/*
********************************************
*  Dimensions used by the Scroller	*
********************************************
*/
var currentContentHeight;				// height of currently-visible content DIV
var currentContentTop;					// top....
var viewHeight;							// height of the parent (overflow:hidden) view
var trackMouseY;						// mouse location in the scroll track
var thumbHeight;						// height of the thumb control
var tracking					= true;	// is the mouse down in the scroll track
var thumbStartY					= -1;	// point where we started scrolling with the thumb
var scrollThumbStartPos			= -1;	// thumb's 'top' value when we started scrolling
var scrollBarHeight;					// height of our scrollbar.  TBD	
var numberOfScrollablePixels;			// for calculating thumb size/position and content position ("page number")

/*
********************************************************
* Constants.  Hardcoded to match respective CSS values	*
********************************************************
*/
var SCROLLBAR_TOP		= -1;
var SCROLL_THUMB_HEIGHT	= 27;
	
// CSS element names of critical DIVs.  Abstracted for easy customization.
var SCROLLBAR_DIV_NAME		= 'myScrollBar';		// Name of the parent scroller div, containing the track and the thumb.
var SCROLLTHUMB_DIV_NAME	= 'myScrollThumb';		// Div containing the scroller thumb
var SCROLLTRACK_DIV_NAME	= 'myScrollTrack'; 		// Div containing the scroller track
var TRACK_TOP_DIV_NAME		= 'myScrollTrackTop';	// Top edge of scroller track
var TRACK_MID_DIV_NAME		= 'myScrollTrackMid';	// variable-size center of scroller track
var TRACK_BOT_DIV_NAME		= 'myScrollTrackBot';	// Bottom edge of scroller track
var BACK_TRACK_TOP_DIV_NAME		= 'otherScrollTrackTop';	// Top edge of scroller track
var BACK_TRACK_MID_DIV_NAME		= 'otherScrollTrackMid';	// variable-size center of scroller track
var BACK_TRACK_BOT_DIV_NAME		= 'otherScrollTrackBot';	// Bottom edge of scroller track
	
var PAGE_SKIP_PAUSE	= 150; // time (ms) between page jumps when holding the mouse down in the track.
	
// Calculate the height of the views and make the thumb proportional.
// If a single scroller is being shared across multiple divs (as in this sample),
// this function must be called whenever the divs swap, to recalibrate the scrollbar.
function calculateAndShowThumb() {

    var contentDiv;

    if("front" == FRONT_OR_BACK){
	contentDiv = document.getElementById("output");

	scrollBar	= document.getElementById(SCROLLBAR_DIV_NAME);
	scrollThumb = document.getElementById(SCROLLTHUMB_DIV_NAME);
	scrollTrack = document.getElementById(SCROLLTRACK_DIV_NAME);

    }else{
	contentDiv = document.getElementById("outputBack");

	scrollBar	= document.getElementById("otherScrollBar");
	scrollThumb = document.getElementById("otherScrollThumb");
	scrollTrack = document.getElementById("otherScrollTrack");

    }

	
    if (contentDiv != null) {
	currentContent = contentDiv;
    } else if (currentContent == null) {
	hideScrollbar();
    }
	
    currentContent.style.display = 'block';
    currentContentStyle = document.defaultView.getComputedStyle(currentContent,'');
    currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
    currentContentHeight = parseInt(currentContentStyle.getPropertyValue('height'));
	
    viewHeight = parseFloat (document.defaultView.getComputedStyle(currentContent.parentNode, '').getPropertyValue('height'));
    scrollBarHeight = parseInt(document.defaultView.getComputedStyle(scrollBar, '').getPropertyValue('height'));

    var percent = getProportion (viewHeight, currentContentHeight);
	
    // hide the scrollbar if all the content is showing.  Determined by the calculated scrollbar height and position.
    if (percent == 0) {
	hideScrollBar();
    } else {
	// Position the thumb according to where the content is currently scrolled.
	// This is necessary for sharing the same scrollbar between multiple content
	// panes that will likely be at different scroll positions.
	thumbHeight = Math.max(Math.round(scrollBarHeight * percent), SCROLL_THUMB_HEIGHT);
	thumbTop = thumbPositionForPagePosition(currentContentTop);
		
	scrollThumb.style.height = thumbHeight + 'px';
	scrollThumb.style.top = thumbTop;
		
	numberOfScrollablePixels = scrollBarHeight - thumbHeight - SCROLLBAR_TOP;
		
	// This is a safeguard so the content matches the new thumb position.  Necessary for live-resizing to work.
	scrollContent(thumbTop);
		
	showScrollBar();
    }
}

// Hide the thumb and track, but keep the parent scrollbar DIV around to preserve formatting
function hideScrollBar() {
    scrollTrack.style.display = 'none';
    scrollThumb.style.display = 'none';
}

function showScrollBar() {
    scrollTrack.style.display = 'block';
    scrollThumb.style.display = 'block';
}

/*
********************************
*	Thumb Scrolling Functions	*
********************************
*/

// This mouseDown is presumably the start of a thumb drag (scroll) action.
function mouseDownScrollThumb (event) {
    // We add these listeners and remove them later; they're only useful while there is mouse activity
    // on the thumb.  This is necessary because there is no mousedrag event in JavaScript.
    document.addEventListener("mousemove", mouseMoveScrollThumb, true);
    document.addEventListener("mouseup", mouseUpScrollThumb, true);
	
    thumbStartY = event.y;

    scrollThumbStartPos = parseInt(document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top'));

}

// At this point we are dragging the scrollThumb.  We know this because the mousemove listener is only installed
// after a mousedown.
function mouseMoveScrollThumb (event) {
    var deltaY = event.y - thumbStartY;
	
    var newPosition = scrollThumbStartPos + deltaY;
    scrollContent(newPosition);
}

function scrollContent(newThumbPosition) {

    // Correct if we're going to clip above the top or below the bottom
    if (newThumbPosition < SCROLLBAR_TOP) {
	newThumbPosition = SCROLLBAR_TOP;
    } else if ((newThumbPosition + thumbHeight) > scrollBarHeight) {
	newThumbPosition = scrollBarHeight - thumbHeight;
    }
		
    scrollThumb.style.top = newThumbPosition + 'px';
	
    currentContentTop = pagePositionForThumbPosition(newThumbPosition);
    currentContent.style.top = currentContentTop + 'px';
}

function mouseUpScrollThumb (event) {
    // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
    document.removeEventListener("mousemove", mouseMoveScrollThumb, true);
    document.removeEventListener("mouseup", mouseUpScrollThumb, true);
	
    // reset the starting position
    thumbStartY = -1;
}

/*
********************************
*	Track Scrolling Functions	*
********************************
*/

function mouseDownTrack (event) {
    updateTrackMouseY(event);
	
    scrollTrack.addEventListener("mousemove", mouseMoveTrack, false);
    scrollTrack.addEventListener("mouseover", mouseOverTrack, false);
    scrollTrack.addEventListener("mouseout", mouseOutTrack, false);
	
    // This is our handling for clicks in the track.
    var thumbTop = document.defaultView.getComputedStyle(scrollThumb,'').getPropertyValue('top');
    if (trackMouseY > parseInt(thumbTop)) {
	pageDown();
	trackTimer = setInterval("pageDown();", PAGE_SKIP_PAUSE);
    } else {
	pageUp();
	trackTimer = setInterval("pageUp();", PAGE_SKIP_PAUSE);
    }
	
}

function mouseMoveTrack(event) {
    // If the mouse moved while being held down, update the location so we 
    // stop the track-scrolling in the right place.
    updateTrackMouseY(event);
}

function mouseOutTrack(event) {
    // When the mouse moves out while pressed, we turn track-scrolling off.
    // The timer keeps firing, but pageUp/pageDown exits based on this value
    tracking = false;
}

function mouseOverTrack(event) {
    // The timer is still firing, but pageUp/pageDown are waiting for the mouse to
    // return to the track.  This will resume track-scrolling.
    tracking = true;
}

function mouseUpTrack(event) {
    // stop track-scrolling
    clearInterval(trackTimer);
	
    // After mouseup, these events are just noise. Remove them; they'll be re-added on the next mouseDown
    scrollTrack.removeEventListener("mousemove", mouseMoveTrack, false);
    scrollTrack.removeEventListener("mouseover", mouseMoveTrack, false);
    scrollTrack.removeEventListener("mouseout", mouseMoveTrack, false);
}

// correct the coordinates for the sourceEvent so they properly match the source component
// **YOU MAY NEED TO UPDATE THIS FUNCTION** depending on how deeply the scrollbar div is nested
function updateTrackMouseY (event) {	
    if (event.toElement.id == 'myScrollTrackMid') {
	// source is the ctr component of the track; offset by the top component.
	var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
	trackMouseY = event.offsetY + parseInt(topHeight);
    } else if (event.toElement.id == TRACK_BOT_DIV_NAME) {
	// source is the bottom component of the track; offset by the top and the middle.
	var midHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_MID_DIV_NAME)).getPropertyValue('height');
	var topHeight = document.defaultView.getComputedStyle(document.getElementById(TRACK_TOP_DIV_NAME)).getPropertyValue('height');
	trackMouseY = event.offsetY + parseInt(midHeight) + parseInt(topHeight);
    } else if (event.toElement.id == BACK_TRACK_BOT_DIV_NAME) {
	// source is the bottom component of the track; offset by the top and the middle.
	var midHeight = document.defaultView.getComputedStyle(document.getElementById(BACK_TRACK_MID_DIV_NAME)).getPropertyValue('height');
	var topHeight = document.defaultView.getComputedStyle(document.getElementById(BACK_TRACK_TOP_DIV_NAME)).getPropertyValue('height');
	trackMouseY = event.offsetY + parseInt(midHeight) + parseInt(topHeight);
    } else {
	// source is the top of the track
	trackMouseY = event.offsetY - (event.toElement.offsetTop + event.toElement.parentNode.offsetTop);
    }	
	
}

/*
********************************************************************
* pageUp/pageDown													*
* Used by track-scrolling code, but can be called independently	*
********************************************************************
*/ 

// Reposition the content one page (viewHeight) upwards.  Prevent out-of-bounds values.
// Remember that the content top becomes increasingly NEGATIVE (moves upwards) as we scroll down.	
function pageDown() {
    if (!tracking) return;
    // calculate the last page.  This is equal to the content's full height, less one viewHeight
    // Again, the value is negative because that's how far offset the content would need to be.
    currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
    var lastPageY = -(currentContentHeight - viewHeight);
    // calculate the next page from the content's current position.
    var nextPageY = currentContentTop - viewHeight;
    currentContentTop = Math.max(lastPageY, nextPageY);
    currentContent.style.top = currentContentTop + 'px';

    // reposition the scroll thumb based on the new page position.
    var newThumbTop = thumbPositionForPagePosition(currentContentTop);
    var thumbBottom = newThumbTop + parseInt(scrollThumb.style.height);
    scrollThumb.style.top = newThumbTop;


    if (trackMouseY < thumbBottom) {
	// the thumb has met the mouse; time to stop track-scrolling.
	clearInterval(trackTimer);
    }
}

// very similar to pageDown, with some values negated to move the content in a different direction.
function pageUp() {
    if (!tracking) return;
    currentContentTop = parseInt(currentContentStyle.getPropertyValue('top'));
    var firstPageY = 0;
    var nextPageY = currentContentTop + viewHeight;
    currentContentTop = Math.min(firstPageY, nextPageY)
	currentContent.style.top = currentContentTop + 'px';
	
    var newThumbTop = thumbPositionForPagePosition(currentContentTop);
    scrollThumb.style.top = newThumbTop;

    if (trackMouseY > newThumbTop) {
	clearInterval(trackTimer);
    }
}

/*
********************************
*	Utility / Math functions	*
********************************
*/
 
function getProportion (viewheight, documentheight) {
    if (documentheight <= viewheight)
	return 0;
    else
	return viewheight/documentheight;
}

// Given the position of the thumb, tell us what the content top should be.
// This is the key value that allows us to thumb-scroll.
function pagePositionForThumbPosition (thumbPosition) {
    return -(thumbPosition - SCROLLBAR_TOP) * ((currentContentHeight - viewHeight) / numberOfScrollablePixels);
}

// Given the position of the page, tell us where the thumb should be.
// This is the key value that allows us to track-scroll.
function thumbPositionForPagePosition (pagePosition) {
    return -(pagePosition / ((currentContentHeight - viewHeight) / numberOfScrollablePixels)) + SCROLLBAR_TOP;
}
